/*
 * @Author: ChenJianHui
 * @Date: 2021-09-27 12:00:00
 * @LastEditors  : ChenJianHui
 * @LastEditTime : 2021-09-27 12:00:00
 * @Description: axios二次封装，含详细注释。
 */

// 引入axios
import axios from "axios";
// 格式化数据的第三方库
import qs from "qs";

/**
 * 环境的配置，区分生产环境和开发环境
 */
switch (process.env.NODE_ENV) {
  case "production":
    // 生产环境，部署到的服务器
    // baseURL配置基地址，配置之后就不需要在每个接口填写完整路径
    axios.defaults.baseURL = "http://生产真实地址";
    break;
  case "test":
    axios.defaults.baseURL = "http://测试环境";
    break;
  default:
    axios.defaults.baseURL = "http://开发环境";
    break;
}

/**
 * 设置超时时间，单位毫秒
 */
axios.defaults.timeout = 50000;

/**
 * 设置是否允许跨域和是否允许携带凭证，如果该设置为false，请求时不允许携带凭证
 */
axios.defaults.withCredentials = true;

/**
 * 设置请求头
 *   - 设置请求传递数据的格式，通常需要包含 `x-www-form-urlencoded` 格式，该格式的数据通常为 `xxx=xxx&yyy=yyy`，这是为了post请求与get请求的格式相同
 */
axios.defaults.headers["Content-Type"] = "application/x-www-form-urlencoded";
// 针对post请求设置数据格式，该方法transformRequest仅针对post请求有效
// 使用qs库将传入的data数据转换为 `x-www-form-urlencoded` 格式
// 当然，这需要看服务器使用什么数据格式，如果服务器使用json，那么不需要这样设置
axios.defaults.transformRequest = data => qs.stringify(data);

/**
 * 设置请求拦截器。它的执行逻辑：
 *   客户端的请求 -> 请求拦截器 -> 服务器
 * 这样可以对请求操作进行一系列的处理，再发送给服务器，比如，添加TOKEN
 */
axios.interceptors.request.use(
  config => {
    // 携带token，从本地读取到token，然后添加到请求头
    const token = localStorage.getItem("token");
    token && (config.headers.Authorization = token);
    return config;
  },
  error => {
    return Promise.reject(error);
  }
);

/**
 * 自定义响应成功的HTTP状态码
 * 通常情况下不需要，因为开发的程序一般不会出现3开头的状态码
 * 如果需要，可以打开注释
 */
// axios.defaults.validateStatus = status => {
//   return /^(2|3)\d{2}$/.test(status);
// };

/**
 * 响应拦截器。它的知性逻辑：
 *   服务器返回信息 -> 相应拦截器 -> 客户端获取到信息
 * 这样可以首先对响应的信息做一些预处理，从而使得客户端的代码更加整洁
 */
axios.interceptors.response.use(
  response => {
    // 成功后返回response的主体，其他数据一般并不需要，这样在使用中只需要关注数据主体就可以
    return response.data;
  },
  error => {
    let { response } = error; // 等效于error.response
    if (response) {
      // 服务器返回了结果
      // 这里能够读出服务器返回的错误HTTP状态码，根据不同状态码进行不同处理
      // 这个根据业务需求操作即可
      switch (response.status) {
        case 401:
          // 权限不够，一般是未登录
          // ...
          break;
        case 403:
          // 服务器已经接受，但是拒绝访问，通常是登录过期
          // ...
          localStorage.removeItem("token");
          break;
        case 404:
          // 找不到资源
          // ...
          break;
      }
    } else {
      // 服务器根本就没有返回任何东西
      // 这里一般只有两种情况，服务器崩溃，客户端没有网。
      // 通常在这里做断网处理
      if (!window.navigator.onLine) {
        // 处理断网
        // ...
        return;
      }
      // 什么都不是，返回一个错误
      return Promise.reject(error);
    }
  }
);

// 都写好，最后将axios导出
export default axios;
